<?php
namespace app\common\controller;
use app\admin\model\AdminGroup;
use app\data\cache\CacheToken;
use app\BaseController;
use ba\Auth;
use think\App;
use think\facade\Db;
use think\model;
use  think\facade\Request;
use \Firebase\JWT\JWT;

class PCBase extends BaseController
{
    public $api_result;

    /**
     * 是否开启模型验证
     * @var bool
     */
    protected bool $modelValidate = true;

    protected bool|string|int $dataLimit = false;

    /**
     * 数据限制开启时自动填充字段值为当前管理员id
     * @var bool
     */
    protected bool $dataLimitFieldAutoFill = true;



    /**
     * 默认排序
     * @var string|array
     */
    protected string|array $defaultSortField = 'id,desc';

    public function __construct(App $app)
    {
        parent::__construct($app);
        $this->api_result = new ApiResult();
        $this->checkAccessToken();
    }

    public function jsonResponse($code = 200, $success = false, $msg = "加载错误", $data = [])
    {
        $obj = new \stdClass();
        $obj->code = $code;
        $obj->success = $success;
        $obj->msg = $msg;
        $obj->data = $data;
        if ($obj) {
            return json($obj);
        } else {
            abort(404);
        }

    }


    public function jsonMessage($code = 200, $success = false, $msg = "加载错误", $data = null)
    {

        $this->api_result->code = $code;
        $this->api_result->success = $success;
        $this->api_result->msg = $msg;
        $this->api_result->data = null;
        if ($data) $this->api_result->data = $data;
        if ($this->api_result) {
            return json($this->api_result);
        } else {
            abort(404);
        }
    }


    public function checkAccessToken()
    {

        $accessToken = Request::header('batoken');
        if (empty($accessToken)) {
            die(json_encode(['code' => 601, 'success' => false, 'msg' => '未授权登录']));
        }
        $decodeinfo = $this->getInfoByToken($accessToken);
        $admin_info = $this->object_to_array($decodeinfo);
        $info = CacheToken::getAdminPcData($admin_info['tel']);
        if ($info != $admin_info['token']) {
            die(json_encode(['code' => 601, 'success' => false, 'msg' => '鉴权无效']));
        }
        return true;
    }


    //获取当前操作员工的tel;
    public function getAdminTel()
    {
        $accessToken = Request::instance()->header('batoken');
        $decodeinfo = $this->getInfoByToken($accessToken);
        //获取员工的tel;
        $admin_info = $this->object_to_array($decodeinfo);
        return $admin_info['tel'];
    }


    //获取当前操作员工的分组ID;
    public function getAdminGroupId()
    {
        $accessToken = Request::instance()->header('batoken');
        $decodeinfo = $this->getInfoByToken($accessToken);
        //获取医生的id;
        $admin_info = $this->object_to_array($decodeinfo);
        return $admin_info['groupId'];
    }


    //获取当前操作员工的ID;
    public function getAdminId()
    {
        $accessToken = Request::instance()->header('batoken');
        $decodeinfo = $this->getInfoByToken($accessToken);
        //获取员工的id;
        $admin_info = $this->object_to_array($decodeinfo);
        return $admin_info['id'];
    }



    //获取当前员工信息
    public function getAdminInfo()
    {
        $accessToken = Request::instance()->header('batoken');
        $decodeinfo = $this->getInfoByToken($accessToken);
        return $this->object_to_array($decodeinfo);
    }

    private function getInfoByToken($token)
    {
        try {
            $decoded = JWT::decode($token, env('JWT_JWT_SALT'), array('HS256'));
            return $decoded;
        } catch (\Exception  $e) {
            die(json_encode(['code' => 601, 'success' => false, 'msg' => '鉴权无效']));
        }
    }

    /**
     * 对象 转 数组
     *
     * @param object $obj 对象
     * @return array
     */
    private function object_to_array($obj)
    {
        $obj = (array)$obj;
        foreach ($obj as $k => $v) {
            if (gettype($v) == 'resource') {
                return;
            }
            if (gettype($v) == 'object' || gettype($v) == 'array') {
                $obj[$k] = (array)object_to_array($v);
            }
        }
        return $obj;
    }


    /**
     * 排除入库字段
     * @param array $params
     * @return array
     */
    protected function excludeFields(array $params): array
    {
        if (!is_array($this->preExcludeFields)) {
            $this->preExcludeFields = explode(',', (string)$this->preExcludeFields);
        }

        foreach ($this->preExcludeFields as $field) {
            if (array_key_exists($field, $params)) {
                unset($params[$field]);
            }
        }
        return $params;
    }

    /**
     * 构建查询参数
     * @throws Throwable
     */
    public function queryBuilder(): array
    {
        if (empty($this->model)) {
            return [];
        }
        $pk           = $this->model->getPk();
        $quickSearch  = $this->request->get("quickSearch/s", '');
        $limit        = $this->request->get("limit/d", 10);
        $order        = $this->request->get("order/s", '');
        $search       = $this->request->get("search/a", []);
        $initKey      = $this->request->get("initKey/s", $pk);
        $initValue    = $this->request->get("initValue", '');
        $initOperator = $this->request->get("initOperator/s", 'in');

        $where              = [];
        $modelTable         = strtolower($this->model->getTable());
        $alias[$modelTable] = parse_name(basename(str_replace('\\', '/', get_class($this->model))));
        $mainTableAlias     = $alias[$modelTable] . '.';

        // 快速搜索
        if ($quickSearch) {
            $quickSearchArr = is_array($this->quickSearchField) ? $this->quickSearchField : explode(',', $this->quickSearchField);
            foreach ($quickSearchArr as $k => $v) {
                $quickSearchArr[$k] = str_contains($v, '.') ? $v : $mainTableAlias . $v;
            }
            $where[] = [implode("|", $quickSearchArr), "LIKE", '%' . str_replace('%', '\%', $quickSearch) . '%'];
        }
        if ($initValue) {
            $where[] = [$initKey, $initOperator, $initValue];
            $limit   = 999999;
        }

        // 排序
        if ($order) {
            $order = explode(',', $order);
            if (!empty($order[0]) && !empty($order[1]) && ($order[1] == 'asc' || $order[1] == 'desc')) {
                $order = [$order[0] => $order[1]];
            }
        } else {
            if (is_array($this->defaultSortField)) {
                $order = $this->defaultSortField;
            } else {
                $order = explode(',', $this->defaultSortField);
                if (!empty($order[0]) && !empty($order[1])) {
                    $order = [$order[0] => $order[1]];
                } else {
                    $order = [$pk => 'desc'];
                }
            }
        }

        // 通用搜索组装
        foreach ($search as $field) {
            if (!is_array($field) || !isset($field['operator']) || !isset($field['field']) || !isset($field['val'])) {
                continue;
            }
            if ($field['field'] == 'shop_name_arr') {
                $field['field'] = 'shop_id';
            }

            $field['operator'] = $this->getOperatorByAlias($field['operator']);

            $fieldName = str_contains($field['field'], '.') ? $field['field'] : $mainTableAlias . $field['field'];

            // 日期时间
            if (isset($field['render']) && $field['render'] == 'datetime') {
                if ($field['operator'] == 'RANGE') {
                    $datetimeArr = explode(',', $field['val']);
                    if (!isset($datetimeArr[1])) {
                        continue;
                    }
                    $datetimeArr = array_filter(array_map("strtotime", $datetimeArr));
                    $where[]     = [$fieldName, str_replace('RANGE', 'BETWEEN', $field['operator']), $datetimeArr];
                    continue;
                }
                $where[] = [$fieldName, '=', strtotime($field['val'])];
                continue;
            }

            // 范围查询
            if ($field['operator'] == 'RANGE' || $field['operator'] == 'NOT RANGE') {
                $arr = explode(',', $field['val']);
                // 重新确定操作符
                if (!isset($arr[0]) || $arr[0] === '') {
                    $operator = $field['operator'] == 'RANGE' ? '<=' : '>';
                    $arr      = $arr[1];
                } elseif (!isset($arr[1]) || $arr[1] === '') {
                    $operator = $field['operator'] == 'RANGE' ? '>=' : '<';
                    $arr      = $arr[0];
                } else {
                    $operator = str_replace('RANGE', 'BETWEEN', $field['operator']);
                }
                $where[] = [$fieldName, $operator, $arr];
                continue;
            }

            switch ($field['operator']) {
                case '=':
                case '<>':
                    $where[] = [$fieldName, $field['operator'], (string)$field['val']];
                    break;
                case 'LIKE':
                case 'NOT LIKE':
                    $where[] = [$fieldName, $field['operator'], '%' . str_replace('%', '\%', $field['val']) . '%'];
                    break;
                case '>':
                case '>=':
                case '<':
                case '<=':
                    $where[] = [$fieldName, $field['operator'], intval($field['val'])];
                    break;
                case 'FIND_IN_SET':
                    if (is_array($field['val'])) {
                        foreach ($field['val'] as $val) {
                            $where[] = [$fieldName, 'find in set', $val];
                        }
                    } else {
                        $where[] = [$fieldName, 'find in set', $field['val']];
                    }
                    break;
                case 'IN':
                case 'NOT IN':
                    $where[] = [$fieldName, $field['operator'], is_array($field['val']) ? $field['val'] : explode(',', $field['val'])];
                    break;
                case 'NULL':
                case 'NOT NULL':
                    $where[] = [$fieldName, strtolower($field['operator']), ''];
                    break;
            }
        }

        // 数据权限
        $dataLimitAdminIds = $this->getDataLimitAdminIds();
        if ($dataLimitAdminIds) {
            $where[] = [$mainTableAlias . $this->dataLimitField, 'in', $dataLimitAdminIds];
        }

        return [$where, $alias, $limit, $order];
    }



    /**
     * 数据权限控制-获取有权限访问的管理员Ids
     * @throws Throwable
     */
    protected function getDataLimitAdminIds(): array
    {
        if (!$this->dataLimit || $this->isSuperAdmin($this->getAdminId())) {
            return [];
        }

        $adminIds = [];
        if ($this->dataLimit == 'parent') {
            // 取得当前管理员的下级分组们
            $parentGroups = $this->getAdminChildGroups();
            if ($parentGroups) {
                // 取得分组内的所有管理员
                $adminIds = $this->getGroupAdmins($parentGroups);
            }
        } elseif (is_numeric($this->dataLimit) && $this->dataLimit > 0) {
            // 在组内，可查看所有，不在组内，可查看自己的
            $adminIds = $this->getGroupAdmins([$this->dataLimit]);
            return in_array($this->getAdminId(), $adminIds) ? [] : [$this->getAdminId()];
        } elseif ($this->dataLimit == 'allAuth' || $this->dataLimit == 'allAuthAndOthers') {
            // 取得拥有他所有权限的分组
            $allAuthGroups = $this->getAllAuthGroups($this->dataLimit);
            // 取得分组内的所有管理员
            $adminIds = $this->getGroupAdmins($allAuthGroups);
        }
        $adminIds[] = $this->getAdminId();
        return array_unique($adminIds);
    }


    /**
     * 从别名获取原始的逻辑运算符
     * @param string $operator 逻辑运算符别名
     * @return string 原始的逻辑运算符，无别名则原样返回
     */
    protected function getOperatorByAlias(string $operator): string
    {
        $alias = [
            'ne'  => '<>',
            'eq'  => '=',
            'gt'  => '>',
            'egt' => '>=',
            'lt'  => '<',
            'elt' => '<=',
        ];

        return $alias[$operator] ?? $operator;
    }

    /**
     * @desc 判断是否是超级管理员
     * @author xx
     * @date 2024-03-02
     */
    public function isSuperAdmin($uid): bool
    {

        return in_array('*', (new Auth())->getRuleIds($uid));
    }

    /**
     * 获取一个分组下的子分组
     * @param int   $groupId  分组ID
     * @param array $children 存放子分组的变量
     * @return void
     * @throws Throwable
     */
    public function getGroupChildGroups(int $groupId, array &$children): void
    {
        $childrenTemp = AdminGroup::where('pid', $groupId)
            ->where('status', '1')
            ->select();
        foreach ($childrenTemp as $item) {
            $children[] = $item['id'];
            $this->getGroupChildGroups($item['id'], $children);
        }
    }


    /**
     * 获取管理员所在分组的所有子级分组
     * @return array
     * @throws Throwable
     */
    public function getAdminChildGroups(): array
    {
        $groupIds = Db::name('admin_group_access')
            ->where('uid', $this->getAdminId())
            ->select();
        $children = [];
        foreach ($groupIds as $group) {
            $this->getGroupChildGroups($group['group_id'], $children);
        }
        return array_unique($children);
    }


    /**
     * 获取分组内的管理员
     * @param array $groups
     * @return array 管理员数组
     */
    public function getGroupAdmins(array $groups): array
    {
        return Db::name('admin_group_access')
            ->where('group_id', 'in', $groups)
            ->column('uid');
    }


    /**
     * 获取拥有"所有权限"的分组
     * @param string $dataLimit 数据权限
     * @return array 分组数组
     * @throws Throwable
     */
    public function getAllAuthGroups(string $dataLimit): array
    {
        // 当前管理员拥有的权限
        $rules         = (new Auth())->getRuleIds($this->getAdminId());
        $allAuthGroups = [];
        $groups        = AdminGroup::where('status', '1')->select();
        foreach ($groups as $group) {
            if ($group['rules'] == '*') {
                continue;
            }
            $groupRules = explode(',', $group['rules']);

            // 及时break, array_diff 等没有 in_array 快
            $all = true;
            foreach ($groupRules as $groupRule) {
                if (!in_array($groupRule, $rules)) {
                    $all = false;
                    break;
                }
            }
            if ($all) {
                if ($dataLimit == 'allAuth' || ($dataLimit == 'allAuthAndOthers' && array_diff($rules, $groupRules))) {
                    $allAuthGroups[] = $group['id'];
                }
            }
        }
        return $allAuthGroups;
    }

}

